Data loading

RQi: Differential expression of metabolites per ApoE genotype

Data Preparation

# Transpose, standardize and store the metabolite data in Y
load("data.Rda")
Y <- as.matrix(ADmets)

Wilcoxon rank-sum test

Triglycerides and diglycerides seem to survive FDR control.

# Store all observations of Class 1 in C1
C1 <- ADmets[geno$APOEb == "E4NO", 1:230]
# Store all observations of Class 2 in C2
C2 <- ADmets[geno$APOEb == "E4YES", 1:230]

# Create a function to perform the Wilcoxon rank-sum (Mann-Whitney U) test on two vectors
MannWhitneyU <- function(x, y) {
  wilcoxon <- wilcox.test(x, y, paired = FALSE, alternative = "less")
  return(wilcoxon$p.value)
}

# Use purrr::map2 to apply the function to corresponding columns
wilcoxons <- purrr::map2(C1[, 1:230], C2[, 1:230], ~ MannWhitneyU(.x, .y))

# Coerce p_values to dataframe and transpose it
p_values <- t(data.frame(wilcoxons))
# Calculate the FDR-adjusted p-values
p_adj <- p.adjust(wilcoxons, method = "fdr")

results <- data.frame(p_values, p_adj)
# Filter out the non-significant (a=0.05) FDR-adjusted p-values
dplyr::filter(results, p_adj < 0.05)

Carriers of one copy of E4 vs two copies tend to have less histamine, fumaric acid, uracil, triglyceride TG.48.0, phosphatidylcholine PC.36.4, with p < 0.05, however these don’t survive FDRcut at 0.95 (p_adj = 0.97)’

geno$g <- geno$APOE
geno$g[geno$g == "E3E4"] <- geno$g[geno$g == "E2E4"] <- "E4x1"
geno$g[geno$g == "E4E4"] <- "E4x2"
# Store all observations of Class 1 in C1
C1 <- ADmets[geno$g == "E4x1", 1:230]
# Store all observations of Class 2 in C2
C2 <- ADmets[geno$g == "E4x2", 1:230]

# Use purrr::map2 to apply the function to corresponding columns
wilcoxons <- purrr::map2(C1[, 1:230], C2[, 1:230], ~ MannWhitneyU(.x, .y))

# Coerce p_values to dataframe and transpose it
p_values <- t(data.frame(wilcoxons))
# Calculate the FDR-adjusted p-values
p_adj <- p.adjust(wilcoxons, method = "fdr")

results <- data.frame(p_values, p_adj)
# Filter out the non-significant (a=0.05) FDR-adjusted p-values
dplyr::filter(results, p_adj < 0.05)

Testing no E4 vs one E4, no metabolites differ significantly

geno$g <- geno$APOE
geno$g[geno$g == "E3E4"] <- geno$g[geno$g == "E2E4"] <- "E4x1"
geno$g[geno$APOEb == "E4NO"] <- "E4x0"
# Store all observations of Class 1 in C1
C1 <- ADmets[geno$g == "E4x1", 1:230]
# Store all observations of Class 2 in C2
C2 <- ADmets[geno$g == "E4x0", 1:230]

# Use purrr::map2 to apply the function to corresponding columns
wilcoxons <- purrr::map2(C1[, 1:230], C2[, 1:230], ~ MannWhitneyU(.x, .y))

# Coerce p_values to dataframe and transpose it
p_values <- t(data.frame(wilcoxons))
# Calculate the FDR-adjusted p-values
p_adj <- p.adjust(wilcoxons, method = "fdr")

results <- data.frame(p_values, p_adj)
# Filter out the non-significant (a=0.05) FDR-adjusted p-values
dplyr::filter(results, p_adj < 0.05)

Global test

thomson <- merge(thomson,y)
# Binary outcome
gt.b <- globaltest::gt(E4 ~ 1, E4 ~ . - APOE - Diagnosis, data = df)

# Multinomial outcome
df$APOE <- as.factor(df$APOE)
gt.m <- globaltest::gt(APOE ~ 1, APOE ~ . - E4, data = df)

RQii - Classification of metabolites on ApoE class

Data Exploration

Function fit

fit <- function(title,
                X,
                y,
                model,
                ctrl = NULL,
                grid = NULL,
                seed = 123, ...) {
  set.seed(seed)
  # Merge X and y into df
  df <- merge.data.frame(X, y)

  # Train the model
  mdl <- caret::train(df[, 1:ncol(X)], df$y,
    method = model,
    tuneGrid = grid,
    trControl = ctrl,
    metric = "ROC",
    preProcess = c("center", "scale"),
    ...
  )
  # Create a confusion matrix and get performance metrics from caret
  obs <- mdl$pred$obs
  preds <- mdl$pred$pred
  cm <- confusionMatrix(obs, preds,
    dnn = c("X0", "X1"), # nolint
    positive = "X1")
  # Predictions
  ys <- as.numeric(obs) -1
  yhats <- mdl$pred$X1
  roc <- roc(ys, yhats,
    levels = c(0, 1),
    ci = TRUE, boot.n = 1000, ci.alpha = 0.95)
  metrics <- data.frame(c(cm$byClass, roc$auc),
    row.names = c(names(cm$byClass), "AUC")
  )
  names(metrics) <- title
  out <- list("metrics" = metrics, "roc" = roc, "model" = mdl)
  return(out)
}

Using 230 metabolites in AD

Binary classification of outcome 0 vs at least 1 E4 allele

load("./data.Rdata")
X <- df[df$Diagnosis == "Probable AD", 1:230]
y <- df[df$Diagnosis == "Probable AD", "E4"]
y <- as.factor(y)
levels(y) <- make.names(levels(y))

# Define the model training parameters, repeated 10-fold cross-valuidation
ctrl <- trainControl(
  method = "repeatedcv",
  number = 10,
  repeats = 1,
  classProbs = TRUE,
  summaryFunction = twoClassSummary,
  savePredictions = TRUE,
  sampling = 'smote',
)
Logistic Regression
lr <- fit(
  title = "Logistic Regression",
  X = X,
  y = y,
  model = "glmnet",
  ctrl = ctrl,
grid = expand.grid(alpha = c(0,1), lambda = c(0.001,100,100)))
Loading required package: recipes

Attaching package: ‘recipes’

The following object is masked from ‘package:stats’:

    step

Setting direction: controls < cases
Decision Tree
XGBoost Forest
#Get a performance metrics table
metrics <- cbind(lr$metrics, tree$metrics, rf$metrics)
metrics
#Plot ROC curves
rocs <- list(lr$roc, tree$roc, rf$roc)
# Generate labels
labels <- paste0(names(metrics), ", AUC = ", paste(round(metrics[12, ], 2)))
# httpgd::hgd()
ggroc(rocs) +
  theme_clean() +
  scale_color_tableau(labels = labels)

Multiclass Classification of number of ApoE E4 alleles based on metabolites

  # Create a confusion matrix and get performance metrics from caret
  obs <- mdl$pred$obs
Error: object 'mdl' not found
ctrl$summaryFunction = multiClassSummary
X <- df[df$Diagnosis == "Probable AD", 1:230]
y <- df[df$Diagnosis == "Probable AD", "APOE"]
y <- as.factor(y)
levels(y) <- make.names(levels(y))
Multinomial Logistic Regression
library(nnet)
mlr <- multifit(
  X = X,
  y = y,
  model = "multinom",
  ctrl = ctrl,
  trace = FALSE,
  tuneLength = 1
)
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
mlr$cm
Confusion Matrix and Statistics

          Reference
Prediction   X0   X1   X2
        X0 1214 1801 2065
        X1 2896 1736 3115
        X2 1338 1238  726

Overall Statistics
                                          
               Accuracy : 0.2279          
                 95% CI : (0.2215, 0.2345)
    No Information Rate : 0.3662          
    P-Value [Acc > NIR] : 1               
                                          
                  Kappa : -0.1414         
                                          
 Mcnemar's Test P-Value : <2e-16          

Statistics by Class:

                     Class: X0 Class: X1 Class: X2
Sensitivity            0.22283    0.3636   0.12293
Specificity            0.63805    0.4706   0.74802
Pos Pred Value         0.23898    0.2241   0.21987
Neg Pred Value         0.61680    0.6374   0.59616
Prevalence             0.33778    0.2961   0.36617
Detection Rate         0.07527    0.1076   0.04501
Detection Prevalence   0.31496    0.4803   0.20472
Balanced Accuracy      0.43044    0.4171   0.43547
Decision Tree
mtree$cm
Confusion Matrix and Statistics

          Reference
Prediction X0 X1 X2
        X0 42 29 49
        X1 57 56 70
        X2 19 29 30

Overall Statistics
                                          
               Accuracy : 0.336           
                 95% CI : (0.2887, 0.3858)
    No Information Rate : 0.3911          
    P-Value [Acc > NIR] : 0.9886          
                                          
                  Kappa : 0.0216          
                                          
 Mcnemar's Test P-Value : 1.477e-08       

Statistics by Class:

                     Class: X0 Class: X1 Class: X2
Sensitivity             0.3559    0.4912   0.20134
Specificity             0.7034    0.5243   0.79310
Pos Pred Value          0.3500    0.3060   0.38462
Neg Pred Value          0.7088    0.7071   0.60726
Prevalence              0.3097    0.2992   0.39108
Detection Rate          0.1102    0.1470   0.07874
Detection Prevalence    0.3150    0.4803   0.20472
Balanced Accuracy       0.5297    0.5078   0.49722
XGBoost Forest
mrf$cm$byClass[3,]
         Sensitivity          Specificity       Pos Pred Value       Neg Pred Value 
          0.13007017           0.75286798           0.23016354           0.60372651 
           Precision               Recall                   F1           Prevalence 
          0.23016354           0.13007017           0.16621104           0.36226672 
      Detection Rate Detection Prevalence    Balanced Accuracy 
          0.04712009           0.20472441           0.44146907 
multimetrics
                           [,1]       [,2]       [,3]
Sensitivity          0.12292584 0.20134228 0.13007017
Specificity          0.74801917 0.79310345 0.75286798
Pos Pred Value       0.21986675 0.38461538 0.23016354
Neg Pred Value       0.59616434 0.60726073 0.60372651
Precision            0.21986675 0.38461538 0.23016354
Recall               0.12292584 0.20134228 0.13007017
F1                   0.15768897 0.26431718 0.16621104
Prevalence           0.36617273 0.39107612 0.36226672
Detection Rate       0.04501209 0.07874016 0.04712009
Detection Prevalence 0.20472441 0.20472441 0.20472441
Balanced Accuracy    0.43547251 0.49722287 0.44146907

Projection to Latent Factors

load("thomson.Rdata")
X <- thomson

Binary classification of outcome 0 vs at least 1 E4 allele

Logistic Regression
lrf <- fit(
  X = X,
  y = y,
  model = "glm",
  ctrl = ctrl,
)
Something is wrong; all the logLoss metric values are missing:
    logLoss         AUC          prAUC        Accuracy       Kappa        Mean_F1    Mean_Sensitivity
 Min.   : NA   Min.   : NA   Min.   : NA   Min.   : NA   Min.   : NA   Min.   : NA   Min.   : NA     
 1st Qu.: NA   1st Qu.: NA   1st Qu.: NA   1st Qu.: NA   1st Qu.: NA   1st Qu.: NA   1st Qu.: NA     
 Median : NA   Median : NA   Median : NA   Median : NA   Median : NA   Median : NA   Median : NA     
 Mean   :NaN   Mean   :NaN   Mean   :NaN   Mean   :NaN   Mean   :NaN   Mean   :NaN   Mean   :NaN     
 3rd Qu.: NA   3rd Qu.: NA   3rd Qu.: NA   3rd Qu.: NA   3rd Qu.: NA   3rd Qu.: NA   3rd Qu.: NA     
 Max.   : NA   Max.   : NA   Max.   : NA   Max.   : NA   Max.   : NA   Max.   : NA   Max.   : NA     
 NA's   :1     NA's   :1     NA's   :1     NA's   :1     NA's   :1     NA's   :1     NA's   :1       
 Mean_Specificity Mean_Pos_Pred_Value Mean_Neg_Pred_Value Mean_Precision  Mean_Recall 
 Min.   : NA      Min.   : NA         Min.   : NA         Min.   : NA    Min.   : NA  
 1st Qu.: NA      1st Qu.: NA         1st Qu.: NA         1st Qu.: NA    1st Qu.: NA  
 Median : NA      Median : NA         Median : NA         Median : NA    Median : NA  
 Mean   :NaN      Mean   :NaN         Mean   :NaN         Mean   :NaN    Mean   :NaN  
 3rd Qu.: NA      3rd Qu.: NA         3rd Qu.: NA         3rd Qu.: NA    3rd Qu.: NA  
 Max.   : NA      Max.   : NA         Max.   : NA         Max.   : NA    Max.   : NA  
 NA's   :1        NA's   :1           NA's   :1           NA's   :1      NA's   :1    
 Mean_Detection_Rate Mean_Balanced_Accuracy
 Min.   : NA         Min.   : NA           
 1st Qu.: NA         1st Qu.: NA           
 Median : NA         Median : NA           
 Mean   :NaN         Mean   :NaN           
 3rd Qu.: NA         3rd Qu.: NA           
 Max.   : NA         Max.   : NA           
 NA's   :1           NA's   :1             
Error: Stopping
Decision Tree
treef <- fit(
  title = "Decision Tree",
  X = X,
  y = y,
  model = "rpart2",
  ctrl = ctrl,
  grid = expand.grid(maxdepth = c(2))
)
XGBoost Forest
param <- data.frame(nrounds = c(10), max_depth = c(2), eta = c(0.3), gamma = c(0), colsample_bytree = 1, min_child_weight = c(1), subsample = c(1))
rff <- fit(title = "XGBoost",
  X = X,
  y = y,
  model = "xgbTree",
  ctrl = ctrl,
  grid = param
)
#Get a performance metrics table
metricsf <- cbind(lrf$metrics, treef$metrics, rff$metrics)
metricsf
#Plot ROC curves
rocsf <- list(lrf$roc, treef$roc, rff$roc)
# Generate labels
labels <- paste0(names(metrics), ", AUC = ", paste(round(metrics[12, ], 2)))
# httpgd::hgd()
ggroc(rocsf) +
  theme_clean() +
  scale_color_tableau(labels = labels)
Multinomial Logistic Regression
mlr$cm
Confusion Matrix and Statistics

          Reference
Prediction X0 X1 X2
        X0 60 32 28
        X1 63 77 43
        X2 24 28 26

Overall Statistics
                                          
               Accuracy : 0.4278          
                 95% CI : (0.3776, 0.4792)
    No Information Rate : 0.3858          
    P-Value [Acc > NIR] : 0.052045        
                                          
                  Kappa : 0.1246          
                                          
 Mcnemar's Test P-Value : 0.003516        

Statistics by Class:

                     Class: X0 Class: X1 Class: X2
Sensitivity             0.4082    0.5620   0.26804
Specificity             0.7436    0.5656   0.81690
Pos Pred Value          0.5000    0.4208   0.33333
Neg Pred Value          0.6667    0.6970   0.76568
Prevalence              0.3858    0.3596   0.25459
Detection Rate          0.1575    0.2021   0.06824
Detection Prevalence    0.3150    0.4803   0.20472
Balanced Accuracy       0.5759    0.5638   0.54247
Decision Tree
mtree <- multifit(
  X = X,
  y = y,
  model = "rpart2",
  ctrl = ctrl,
  tuneLength = 3
)
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
mtree$cm
Confusion Matrix and Statistics

          Reference
Prediction X0 X1 X2
        X0 37 35 48
        X1 56 58 69
        X2 18 27 33

Overall Statistics
                                          
               Accuracy : 0.336           
                 95% CI : (0.2887, 0.3858)
    No Information Rate : 0.3937          
    P-Value [Acc > NIR] : 0.9913          
                                          
                  Kappa : 0.0182          
                                          
 Mcnemar's Test P-Value : 4.932e-08       

Statistics by Class:

                     Class: X0 Class: X1 Class: X2
Sensitivity            0.33333    0.4833   0.22000
Specificity            0.69259    0.5211   0.80519
Pos Pred Value         0.30833    0.3169   0.42308
Neg Pred Value         0.71648    0.6869   0.61386
Prevalence             0.29134    0.3150   0.39370
Detection Rate         0.09711    0.1522   0.08661
Detection Prevalence   0.31496    0.4803   0.20472
Balanced Accuracy      0.51296    0.5022   0.51260
XGBoost Forest
mrf <- multifit(
  X = X,
  y = y,
  model = "xgbTree",
  ctrl = ctrl,
  grid = param
)
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
mrf$cm
Confusion Matrix and Statistics

          Reference
Prediction X0 X1 X2
        X0 15 12 13
        X1 19 24 18
        X2  7 12  7

Overall Statistics
                                          
               Accuracy : 0.3622          
                 95% CI : (0.2788, 0.4522)
    No Information Rate : 0.378           
    P-Value [Acc > NIR] : 0.6741          
                                          
                  Kappa : 0.0271          
                                          
 Mcnemar's Test P-Value : 0.2052          

Statistics by Class:

                     Class: X0 Class: X1 Class: X2
Sensitivity             0.3659    0.5000   0.18421
Specificity             0.7093    0.5316   0.78652
Pos Pred Value          0.3750    0.3934   0.26923
Neg Pred Value          0.7011    0.6364   0.69307
Prevalence              0.3228    0.3780   0.29921
Detection Rate          0.1181    0.1890   0.05512
Detection Prevalence    0.3150    0.4803   0.20472
Balanced Accuracy       0.5376    0.5158   0.48536
#Get a performance metrics table
multimetricsf <- cbind(mlrf$cm$byClass[3,], mtreef$cm$byClass[3,], mrff$cm$byClass[3,])
multimetricsf
#Plot ROC curves
mrocs <- list(mlrf$roc, mtreef$roc, mrff$roc)
# Generate labels
labels <- paste0(names(metrics), ", AUC = ", paste(round(metrics[12, ], 2)))
# httpgd::hgd()
ggroc(mrocs) +
  theme_clean() +
  scale_color_tableau(labels = labels)
LS0tCnRpdGxlOiAiQW5hbHlzaXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRoZW1lOiBkYXJrbHkKICAgIGZpZ193aWR0aDogNgogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdGhlbWU6IGRhcmtseQogICAgdG9jX2RlcHRoOiA1CiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbW1lbnQgPSAiIiwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDYsIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKYGBge3IgcGFja2FnZXMsIGluY2x1ZGU9RkFMU0V9CnJlcXVpcmUoY2FyZXQpCnJlcXVpcmUoZHBseXIpCnJlcXVpcmUoZTEwNzEpCnJlcXVpcmUoRk1yYWRpbykKcmVxdWlyZShwUk9DKQpyZXF1aXJlKHJhZ3MycmlkZ2VzKQpyZXF1aXJlKHhnYm9vc3QpCnJlcXVpcmUoZ2d0aGVtZXMpCnJlcXVpcmUoRE13UjIpCnJlcXVpcmUoZ2xvYmFsdGVzdCkKcmVxdWlyZShoZWF0bWFwbHkpCnJlcXVpcmUoaHR0cGdkKQpgYGAKCiMjIERhdGEgbG9hZGluZwoKYGBge3IgZGF0YSBsb2FkaW5nLCBlY2hvPUZBTFNFfQojIEludm9rZSBkYXRhCmxvYWQoIkFEZGF0YS5SZGF0YSIpCmBgYAoKIyMgUlFpOiBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiBtZXRhYm9saXRlcyBwZXIgQXBvRSBnZW5vdHlwZQoKIyMjIERhdGEgUHJlcGFyYXRpb24KCmBgYHtyIFl9CiMgVHJhbnNwb3NlLCBzdGFuZGFyZGl6ZSBhbmQgc3RvcmUgdGhlIG1ldGFib2xpdGUgZGF0YSBpbiBZCmxvYWQoImRhdGEuUmRhIikKWSA8LSBhcy5tYXRyaXgoQURtZXRzKQpgYGAKCiMjIyBXaWxjb3hvbiByYW5rLXN1bSB0ZXN0CgpUcmlnbHljZXJpZGVzIGFuZCBkaWdseWNlcmlkZXMgc2VlbSB0byBzdXJ2aXZlIEZEUiBjb250cm9sLgoKYGBge3Igd2lsY294b24gRTR5ZXMgdnMgRTRub30KIyBTdG9yZSBhbGwgb2JzZXJ2YXRpb25zIG9mIENsYXNzIDEgaW4gQzEKQzEgPC0gQURtZXRzW2dlbm8kQVBPRWIgPT0gIkU0Tk8iLCAxOjIzMF0KIyBTdG9yZSBhbGwgb2JzZXJ2YXRpb25zIG9mIENsYXNzIDIgaW4gQzIKQzIgPC0gQURtZXRzW2dlbm8kQVBPRWIgPT0gIkU0WUVTIiwgMToyMzBdCgojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIHBlcmZvcm0gdGhlIFdpbGNveG9uIHJhbmstc3VtIChNYW5uLVdoaXRuZXkgVSkgdGVzdCBvbiB0d28gdmVjdG9ycwpNYW5uV2hpdG5leVUgPC0gZnVuY3Rpb24oeCwgeSkgewogIHdpbGNveG9uIDwtIHdpbGNveC50ZXN0KHgsIHksIHBhaXJlZCA9IEZBTFNFLCBhbHRlcm5hdGl2ZSA9ICJsZXNzIikKICByZXR1cm4od2lsY294b24kcC52YWx1ZSkKfQoKIyBVc2UgcHVycnI6Om1hcDIgdG8gYXBwbHkgdGhlIGZ1bmN0aW9uIHRvIGNvcnJlc3BvbmRpbmcgY29sdW1ucwp3aWxjb3hvbnMgPC0gcHVycnI6Om1hcDIoQzFbLCAxOjIzMF0sIEMyWywgMToyMzBdLCB+IE1hbm5XaGl0bmV5VSgueCwgLnkpKQoKIyBDb2VyY2UgcF92YWx1ZXMgdG8gZGF0YWZyYW1lIGFuZCB0cmFuc3Bvc2UgaXQKcF92YWx1ZXMgPC0gdChkYXRhLmZyYW1lKHdpbGNveG9ucykpCiMgQ2FsY3VsYXRlIHRoZSBGRFItYWRqdXN0ZWQgcC12YWx1ZXMKcF9hZGogPC0gcC5hZGp1c3Qod2lsY294b25zLCBtZXRob2QgPSAiZmRyIikKCnJlc3VsdHMgPC0gZGF0YS5mcmFtZShwX3ZhbHVlcywgcF9hZGopCiMgRmlsdGVyIG91dCB0aGUgbm9uLXNpZ25pZmljYW50IChhPTAuMDUpIEZEUi1hZGp1c3RlZCBwLXZhbHVlcwpkcGx5cjo6ZmlsdGVyKHJlc3VsdHMsIHBfYWRqIDwgMC4wNSkKYGBgCgpDYXJyaWVycyBvZiBvbmUgY29weSBvZiBFNCB2cyB0d28gY29waWVzIHRlbmQgdG8gaGF2ZSBsZXNzIGhpc3RhbWluZSwgZnVtYXJpYyBhY2lkLCB1cmFjaWwsIHRyaWdseWNlcmlkZSBURy40OC4wLCBwaG9zcGhhdGlkeWxjaG9saW5lIFBDLjM2LjQsIHdpdGggcCBcPCAwLjA1LCBob3dldmVyIHRoZXNlIGRvbid0IHN1cnZpdmUgRkRSY3V0IGF0IDAuOTUgKHBfYWRqID0gMC45NyknCgpgYGB7ciBFNHgxIHZzIEU0eDJ9Cmdlbm8kZyA8LSBnZW5vJEFQT0UKZ2VubyRnW2dlbm8kZyA9PSAiRTNFNCJdIDwtIGdlbm8kZ1tnZW5vJGcgPT0gIkUyRTQiXSA8LSAiRTR4MSIKZ2VubyRnW2dlbm8kZyA9PSAiRTRFNCJdIDwtICJFNHgyIgojIFN0b3JlIGFsbCBvYnNlcnZhdGlvbnMgb2YgQ2xhc3MgMSBpbiBDMQpDMSA8LSBBRG1ldHNbZ2VubyRnID09ICJFNHgxIiwgMToyMzBdCiMgU3RvcmUgYWxsIG9ic2VydmF0aW9ucyBvZiBDbGFzcyAyIGluIEMyCkMyIDwtIEFEbWV0c1tnZW5vJGcgPT0gIkU0eDIiLCAxOjIzMF0KCiMgVXNlIHB1cnJyOjptYXAyIHRvIGFwcGx5IHRoZSBmdW5jdGlvbiB0byBjb3JyZXNwb25kaW5nIGNvbHVtbnMKd2lsY294b25zIDwtIHB1cnJyOjptYXAyKEMxWywgMToyMzBdLCBDMlssIDE6MjMwXSwgfiBNYW5uV2hpdG5leVUoLngsIC55KSkKCiMgQ29lcmNlIHBfdmFsdWVzIHRvIGRhdGFmcmFtZSBhbmQgdHJhbnNwb3NlIGl0CnBfdmFsdWVzIDwtIHQoZGF0YS5mcmFtZSh3aWxjb3hvbnMpKQojIENhbGN1bGF0ZSB0aGUgRkRSLWFkanVzdGVkIHAtdmFsdWVzCnBfYWRqIDwtIHAuYWRqdXN0KHdpbGNveG9ucywgbWV0aG9kID0gImZkciIpCgpyZXN1bHRzIDwtIGRhdGEuZnJhbWUocF92YWx1ZXMsIHBfYWRqKQojIEZpbHRlciBvdXQgdGhlIG5vbi1zaWduaWZpY2FudCAoYT0wLjA1KSBGRFItYWRqdXN0ZWQgcC12YWx1ZXMKZHBseXI6OmZpbHRlcihyZXN1bHRzLCBwX2FkaiA8IDAuMDUpCmBgYAoKVGVzdGluZyBubyBFNCB2cyBvbmUgRTQsIG5vIG1ldGFib2xpdGVzIGRpZmZlciBzaWduaWZpY2FudGx5CgpgYGB7ciBFNG5vIHZzIEU0eDF9Cmdlbm8kZyA8LSBnZW5vJEFQT0UKZ2VubyRnW2dlbm8kZyA9PSAiRTNFNCJdIDwtIGdlbm8kZ1tnZW5vJGcgPT0gIkUyRTQiXSA8LSAiRTR4MSIKZ2VubyRnW2dlbm8kQVBPRWIgPT0gIkU0Tk8iXSA8LSAiRTR4MCIKIyBTdG9yZSBhbGwgb2JzZXJ2YXRpb25zIG9mIENsYXNzIDEgaW4gQzEKQzEgPC0gQURtZXRzW2dlbm8kZyA9PSAiRTR4MSIsIDE6MjMwXQojIFN0b3JlIGFsbCBvYnNlcnZhdGlvbnMgb2YgQ2xhc3MgMiBpbiBDMgpDMiA8LSBBRG1ldHNbZ2VubyRnID09ICJFNHgwIiwgMToyMzBdCgojIFVzZSBwdXJycjo6bWFwMiB0byBhcHBseSB0aGUgZnVuY3Rpb24gdG8gY29ycmVzcG9uZGluZyBjb2x1bW5zCndpbGNveG9ucyA8LSBwdXJycjo6bWFwMihDMVssIDE6MjMwXSwgQzJbLCAxOjIzMF0sIH4gTWFubldoaXRuZXlVKC54LCAueSkpCgojIENvZXJjZSBwX3ZhbHVlcyB0byBkYXRhZnJhbWUgYW5kIHRyYW5zcG9zZSBpdApwX3ZhbHVlcyA8LSB0KGRhdGEuZnJhbWUod2lsY294b25zKSkKIyBDYWxjdWxhdGUgdGhlIEZEUi1hZGp1c3RlZCBwLXZhbHVlcwpwX2FkaiA8LSBwLmFkanVzdCh3aWxjb3hvbnMsIG1ldGhvZCA9ICJmZHIiKQoKcmVzdWx0cyA8LSBkYXRhLmZyYW1lKHBfdmFsdWVzLCBwX2FkaikKIyBGaWx0ZXIgb3V0IHRoZSBub24tc2lnbmlmaWNhbnQgKGE9MC4wNSkgRkRSLWFkanVzdGVkIHAtdmFsdWVzCmRwbHlyOjpmaWx0ZXIocmVzdWx0cywgcF9hZGogPCAwLjA1KQpgYGAKCiMjIyBHbG9iYWwgdGVzdAoKYGBge3J9CnRob21zb24gPC0gbWVyZ2UodGhvbXNvbix5KQojIEJpbmFyeSBvdXRjb21lCmd0LmIgPC0gZ2xvYmFsdGVzdDo6Z3QoRTQgfiAxLCBFNCB+IC4gLSBBUE9FIC0gRGlhZ25vc2lzLCBkYXRhID0gZGYpCgojIE11bHRpbm9taWFsIG91dGNvbWUKZGYkQVBPRSA8LSBhcy5mYWN0b3IoZGYkQVBPRSkKZ3QubSA8LSBnbG9iYWx0ZXN0OjpndChBUE9FIH4gMSwgQVBPRSB+IC4gLSBFNCwgZGF0YSA9IGRmKQpgYGAKCiMjIFJRaWkgLSBDbGFzc2lmaWNhdGlvbiBvZiBtZXRhYm9saXRlcyBvbiBBcG9FIGNsYXNzCgojIyMgRGF0YSBFeHBsb3JhdGlvbgoKYGBge3J9CmxvYWQoIi4vZGF0YS5SZGF0YSIpClggPC0gZGZbZGYkRGlhZ25vc2lzID09ICJQcm9iYWJsZSBBRCIsIDE6MjMwXQpDIDwtIHJvdW5kKGNvcihYKSwgMikKCmhlYXRtYXBseV9jb3IoQywgY29sb3IgPSB2aXJpZGlzLCBwbG90X21ldGhvZCA9ICJwbG90bHkiLCBkZW5kcm9ncmFtID0gRiwgcmVvcmRlcmZ1biA9IHNvcnQuZGVmYXVsdChkLHcpLCBtYWluID0gIkNvcnJlbGF0aW9uIEhlYXRtYXAiLCBmaWxlID0gImhlYXRtYXAuaHRtbCIsIGNvbG9yYmFyX3RoaWNrbmVzcyA9IDE1LCBjb2xvcmJhcl9sZW4gPSAwLjUpCmBgYAoKIyMjIEZ1bmN0aW9uIGZpdAoKYGBge3IgZml0X2RlZn0KZml0IDwtIGZ1bmN0aW9uKHRpdGxlLAogICAgICAgICAgICAgICAgWCwKICAgICAgICAgICAgICAgIHksCiAgICAgICAgICAgICAgICBtb2RlbCwKICAgICAgICAgICAgICAgIGN0cmwgPSBOVUxMLAogICAgICAgICAgICAgICAgZ3JpZCA9IE5VTEwsCiAgICAgICAgICAgICAgICBzZWVkID0gMTIzLCAuLi4pIHsKICBzZXQuc2VlZChzZWVkKQogICMgTWVyZ2UgWCBhbmQgeSBpbnRvIGRmCiAgZGYgPC0gbWVyZ2UuZGF0YS5mcmFtZShYLCB5KQoKICAjIFRyYWluIHRoZSBtb2RlbAogIG1kbCA8LSBjYXJldDo6dHJhaW4oZGZbLCAxOm5jb2woWCldLCBkZiR5LAogICAgbWV0aG9kID0gbW9kZWwsCiAgICB0dW5lR3JpZCA9IGdyaWQsCiAgICB0ckNvbnRyb2wgPSBjdHJsLAogICAgbWV0cmljID0gIlJPQyIsCiAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksCiAgICAuLi4KICApCiAgIyBDcmVhdGUgYSBjb25mdXNpb24gbWF0cml4IGFuZCBnZXQgcGVyZm9ybWFuY2UgbWV0cmljcyBmcm9tIGNhcmV0CiAgb2JzIDwtIG1kbCRwcmVkJG9icwogIHByZWRzIDwtIG1kbCRwcmVkJHByZWQKICBjbSA8LSBjb25mdXNpb25NYXRyaXgob2JzLCBwcmVkcywKICAgIGRubiA9IGMoIlgwIiwgIlgxIiksICMgbm9saW50CiAgICBwb3NpdGl2ZSA9ICJYMSIpCiAgIyBQcmVkaWN0aW9ucwogIHlzIDwtIGFzLm51bWVyaWMob2JzKSAtMQogIHloYXRzIDwtIG1kbCRwcmVkJFgxCiAgcm9jIDwtIHJvYyh5cywgeWhhdHMsCiAgICBsZXZlbHMgPSBjKDAsIDEpLAogICAgY2kgPSBUUlVFLCBib290Lm4gPSAxMDAwLCBjaS5hbHBoYSA9IDAuOTUpCiAgbWV0cmljcyA8LSBkYXRhLmZyYW1lKGMoY20kYnlDbGFzcywgcm9jJGF1YyksCiAgICByb3cubmFtZXMgPSBjKG5hbWVzKGNtJGJ5Q2xhc3MpLCAiQVVDIikKICApCiAgbmFtZXMobWV0cmljcykgPC0gdGl0bGUKICBvdXQgPC0gbGlzdCgibWV0cmljcyIgPSBtZXRyaWNzLCAicm9jIiA9IHJvYywgIm1vZGVsIiA9IG1kbCkKICByZXR1cm4ob3V0KQp9CmBgYAoKIyMjIFVzaW5nIDIzMCBtZXRhYm9saXRlcyBpbiBBRAoKIyMjIyBCaW5hcnkgY2xhc3NpZmljYXRpb24gb2Ygb3V0Y29tZSAwIHZzIGF0IGxlYXN0IDEgRTQgYWxsZWxlCgpgYGB7ciBiaW5hcnlfcHJlcGFyYXRpb259CmxvYWQoIi4vZGF0YS5SZGF0YSIpClggPC0gZGZbZGYkRGlhZ25vc2lzID09ICJQcm9iYWJsZSBBRCIsIDE6MjMwXQp5IDwtIGRmW2RmJERpYWdub3NpcyA9PSAiUHJvYmFibGUgQUQiLCAiRTQiXQp5IDwtIGFzLmZhY3Rvcih5KQpsZXZlbHMoeSkgPC0gbWFrZS5uYW1lcyhsZXZlbHMoeSkpCgojIERlZmluZSB0aGUgbW9kZWwgdHJhaW5pbmcgcGFyYW1ldGVycywgcmVwZWF0ZWQgMTAtZm9sZCBjcm9zcy12YWx1aWRhdGlvbgpjdHJsIDwtIHRyYWluQ29udHJvbCgKICBtZXRob2QgPSAicmVwZWF0ZWRjdiIsCiAgbnVtYmVyID0gMTAsCiAgcmVwZWF0cyA9IDEsCiAgY2xhc3NQcm9icyA9IFRSVUUsCiAgc3VtbWFyeUZ1bmN0aW9uID0gdHdvQ2xhc3NTdW1tYXJ5LAogIHNhdmVQcmVkaWN0aW9ucyA9IFRSVUUsCiAgc2FtcGxpbmcgPSAnc21vdGUnLAopCmBgYAoKIyMjIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKYGBge3J9CmxyIDwtIGZpdCgKICB0aXRsZSA9ICJMb2dpc3RpYyBSZWdyZXNzaW9uIiwKICBYID0gWCwKICB5ID0geSwKICBtb2RlbCA9ICJnbG1uZXQiLAogIGN0cmwgPSBjdHJsLAogIGdyaWQgPSBleHBhbmQuZ3JpZChhbHBoYSA9IGMoMCwxKSwgbGFtYmRhID0gYygwLjAwMSwxMDAsMTAwKSkpCmBgYAoKIyMjIyMgRGVjaXNpb24gVHJlZQoKYGBge3IgfQp0cmVlIDwtIGZpdCgKICB0aXRsZSA9ICJEZWNpc2lvbiBUcmVlIiwKICBYID0gWCwKICB5ID0geSwKICBtb2RlbCA9ICJycGFydDIiLAogIGN0cmwgPSBjdHJsLAogIGdyaWQgPSBleHBhbmQuZ3JpZChtYXhkZXB0aCA9IGMoMikpCikKdHJlZSRtZXRyaWNzCmBgYAoKIyMjIyMgWEdCb29zdCBGb3Jlc3QKCmBgYHtyfQpwYXJhbSA8LSBkYXRhLmZyYW1lKG5yb3VuZHMgPSBjKDEwKSwgbWF4X2RlcHRoID0gYygyKSwgZXRhID0gYygwLjMpLCBnYW1tYSA9IGMoMCksIGNvbHNhbXBsZV9ieXRyZWUgPSBjKDAuNSksIG1pbl9jaGlsZF93ZWlnaHQgPSBjKDEpLCBzdWJzYW1wbGUgPSBjKDEpKQpyZiA8LSBmaXQodGl0bGUgPSAiWEdCb29zdCIsCiAgWCA9IFgsCiAgeSA9IHksCiAgbW9kZWwgPSAieGdiVHJlZSIsCiAgY3RybCA9IGN0cmwsCiAgZ3JpZCA9IHBhcmFtCikKcmYkbWV0cmljcwpgYGAKCmBgYHtyfQojR2V0IGEgcGVyZm9ybWFuY2UgbWV0cmljcyB0YWJsZQptZXRyaWNzIDwtIGNiaW5kKGxyJG1ldHJpY3MsIHRyZWUkbWV0cmljcywgcmYkbWV0cmljcykKbWV0cmljcwojUGxvdCBST0MgY3VydmVzCnJvY3MgPC0gbGlzdChsciRyb2MsIHRyZWUkcm9jLCByZiRyb2MpCiMgR2VuZXJhdGUgbGFiZWxzCmxhYmVscyA8LSBwYXN0ZTAobmFtZXMobWV0cmljcyksICIsIEFVQyA9ICIsIHBhc3RlKHJvdW5kKG1ldHJpY3NbMTIsIF0sIDIpKSkKIyBodHRwZ2Q6OmhnZCgpCmdncm9jKHJvY3MpICsKICB0aGVtZV9jbGVhbigpICsKICBzY2FsZV9jb2xvcl90YWJsZWF1KGxhYmVscyA9IGxhYmVscykKYGBgCgojIyMjIE11bHRpY2xhc3MgQ2xhc3NpZmljYXRpb24gb2YgbnVtYmVyIG9mIEFwb0UgRTQgYWxsZWxlcyBiYXNlZCBvbiBtZXRhYm9saXRlcwoKYGBge3IgbXVsdGlmaXR9Cm11bHRpZml0IDwtIGZ1bmN0aW9uKAogICAgICAgICAgICAgICAgWCwKICAgICAgICAgICAgICAgIHksCiAgICAgICAgICAgICAgICBtb2RlbCwKICAgICAgICAgICAgICAgIGN0cmwgPSBOVUxMLAogICAgICAgICAgICAgICAgZ3JpZCA9IE5VTEwsCiAgICAgICAgICAgICAgICBzZWVkID0gMTIzLCAuLi4pIHsKICBzZXQuc2VlZChzZWVkKQogICMgTWVyZ2UgWCBhbmQgeSBpbnRvIGRmCiAgZGYgPC0gbWVyZ2UuZGF0YS5mcmFtZShYLCB5KQoKICAjIFRyYWluIHRoZSBtb2RlbAogIG1kbCA8LSBjYXJldDo6dHJhaW4oZGZbLCAxOm5jb2woWCldLCBkZiR5LAogICAgbWV0aG9kID0gbW9kZWwsCiAgICB0dW5lR3JpZCA9IGdyaWQsCiAgICB0ckNvbnRyb2wgPSBjdHJsLAogICAgbWV0cmljID0gImFjY3VyYWN5IiwKICAgIC4uLgogICkKICAjIENyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggYW5kIGdldCBwZXJmb3JtYW5jZSBtZXRyaWNzIGZyb20gY2FyZXQKICBvYnMgPC0gbWRsJHByZWQkb2JzCiAgcHJlZHMgPC0gbWRsJHByZWQkcHJlZAogIGNtIDwtIGNvbmZ1c2lvbk1hdHJpeChvYnMsIHByZWRzKQogICMgUHJlZGljdGlvbnMKICB5cyA8LSBhcy5udW1lcmljKG9icykgLTEKICB5aGF0cyA8LSBhcy5udW1lcmljKHByZWRzKSAtMQogIHJvYyA8LSBtdWx0aWNsYXNzLnJvYyh5cywgeWhhdHMpCiAgb3V0IDwtIGxpc3QoImNtIiA9IGNtLCAicm9jIiA9IHJvYywgIm1vZGVsIiA9IG1kbCkKICByZXR1cm4ob3V0KQp9CmBgYAoKYGBge3IgbXVsdGlfcHJlcGFyYXRpb259CmN0cmwkc3VtbWFyeUZ1bmN0aW9uID0gbXVsdGlDbGFzc1N1bW1hcnkKWCA8LSBkZltkZiREaWFnbm9zaXMgPT0gIlByb2JhYmxlIEFEIiwgMToyMzBdCnkgPC0gZGZbZGYkRGlhZ25vc2lzID09ICJQcm9iYWJsZSBBRCIsICJBUE9FIl0KeSA8LSBhcy5mYWN0b3IoeSkKbGV2ZWxzKHkpIDwtIG1ha2UubmFtZXMobGV2ZWxzKHkpKQpgYGAKCiMjIyMjIE11bHRpbm9taWFsIExvZ2lzdGljIFJlZ3Jlc3Npb24KCmBgYHtyIG1sb2dpdH0KbGlicmFyeShubmV0KQptbHIgPC0gbXVsdGlmaXQoCiAgWCA9IFgsCiAgeSA9IHksCiAgbW9kZWwgPSAibXVsdGlub20iLAogIGN0cmwgPSBjdHJsLAogIHRyYWNlID0gRkFMU0UsCiAgdHVuZUxlbmd0aCA9IDEKKQptbHIkY20KYGBgCgojIyMjIyBEZWNpc2lvbiBUcmVlCgpgYGB7ciB9Cm10cmVlIDwtIG11bHRpZml0KAogIFggPSBYLAogIHkgPSB5LAogIG1vZGVsID0gInJwYXJ0MiIsCiAgY3RybCA9IGN0cmwsCiAgdHVuZUxlbmd0aCA9IDMKKQptdHJlZSRjbQpgYGAKCiMjIyMjIFhHQm9vc3QgRm9yZXN0CgpgYGB7ciBtdWx0aV94Z2J9Cm1yZiA8LSBtdWx0aWZpdCgKICBYID0gWCwKICB5ID0geSwKICBtb2RlbCA9ICJ4Z2JUcmVlIiwKICBjdHJsID0gY3RybCwKICBncmlkID0gcGFyYW0KKQptcmYkY20KYGBgCgpgYGB7cn0KI0dldCBhIHBlcmZvcm1hbmNlIG1ldHJpY3MgdGFibGUKbXVsdGltZXRyaWNzIDwtIGNiaW5kKG1sciRjbSRieUNsYXNzWzMsXSwgbXRyZWUkY20kYnlDbGFzc1szLF0sIG1yZiRjbSRieUNsYXNzWzMsXSkKbXVsdGltZXRyaWNzCiNQbG90IFJPQyBjdXJ2ZXMKbXJvY3MgPC0gbGlzdChtbHIkcm9jLCBtdHJlZSRyb2MsIG1yZiRyb2MpCiMgR2VuZXJhdGUgbGFiZWxzCmxhYmVscyA8LSBwYXN0ZTAobmFtZXMobWV0cmljcyksICIsIEFVQyA9ICIsIHBhc3RlKHJvdW5kKG1ldHJpY3NbMTIsIF0sIDIpKSkKIyBodHRwZ2Q6OmhnZCgpCmdncm9jKG1yb2NzKSArCiAgdGhlbWVfY2xlYW4oKSArCiAgc2NhbGVfY29sb3JfdGFibGVhdShsYWJlbHMgPSBsYWJlbHMpCmBgYAoKCiMjIyBQcm9qZWN0aW9uIHRvIExhdGVudCBGYWN0b3JzCgpgYGB7ciBnZXRfdGhvbXNvbiwgZXZhbCA9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnByb2plY3QgPC0gZnVuY3Rpb24oWCwgbSwgc2VlZCkgewogIHNldC5zZWVkKHNlZWQpCiAgWCA8LSBzY2FsZShhcy5tYXRyaXgoWCkpCiAgY292IDwtIGNvcihYKQoKICAjIEZpbmQgcmVkdW5kYW50IGZlYXR1cmVzCiAgZmlsdGVyIDwtIFJGKGNvdikKCiAgIyBGaWx0ZXIgb3V0IHJlZHVuZGFudCBmZWF0dXJlcwogIGZpbHRlcmVkIDwtIHN1YlNldChYLCBmaWx0ZXIpCgogICMgUmVndWxhcml6ZWQgY29ycmVsYXRpb24gbWF0cml4IGVzdGltYXRpb24KICBNIDwtIHJlZ2NvcihmaWx0ZXJlZCkKCiAgIyBHZXQgdGhlIHJlZ3VsYXJpemVkIGNvcnJlbGF0aW9uIG1hdHJpeCBvZiB0aGUgZmlsdGVyZWQgZGF0YXNldAogIFIgPC0gTSRvcHRDb3IKCiAgbWxmYSA8LSBtbEZBKFIsIG0gPSBtKQogIHRob21zb24gPC0gZmFjU2NvcmUoZmlsdGVyZWQsIG1sZmEkTG9hZGluZ3MsIG1sZmEkVW5pcXVlbmVzcykKICByZXR1cm4odGhvbXNvbikKfQpgYGAKCmBgYHtyfQpsb2FkKCJ0aG9tc29uLlJkYXRhIikKWCA8LSB0aG9tc29uCmBgYAojIyMjIEJpbmFyeSBjbGFzc2lmaWNhdGlvbiBvZiBvdXRjb21lIDAgdnMgYXQgbGVhc3QgMSBFNCBhbGxlbGUKIyMjIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKYGBge3J9CmN0cmwkc3VtbWFyeUZ1bmN0aW9uIDwtIHR3b0NsYXNzU3VtbWFyeQpscmYgPC0gZml0KAogIFggPSBYLAogIHkgPSB5LAogIG1vZGVsID0gImdsbSIsCiAgY3RybCA9IGN0cmwsCikKYGBgCgojIyMjIyBEZWNpc2lvbiBUcmVlCgpgYGB7ciB9CnRyZWVmIDwtIGZpdCgKICB0aXRsZSA9ICJEZWNpc2lvbiBUcmVlIiwKICBYID0gWCwKICB5ID0geSwKICBtb2RlbCA9ICJycGFydDIiLAogIGN0cmwgPSBjdHJsLAogIGdyaWQgPSBleHBhbmQuZ3JpZChtYXhkZXB0aCA9IGMoMikpCikKYGBgCgojIyMjIyBYR0Jvb3N0IEZvcmVzdAoKYGBge3J9CnBhcmFtIDwtIGRhdGEuZnJhbWUobnJvdW5kcyA9IGMoMTApLCBtYXhfZGVwdGggPSBjKDIpLCBldGEgPSBjKDAuMyksIGdhbW1hID0gYygwKSwgY29sc2FtcGxlX2J5dHJlZSA9IDEsIG1pbl9jaGlsZF93ZWlnaHQgPSBjKDEpLCBzdWJzYW1wbGUgPSBjKDEpKQpyZmYgPC0gZml0KHRpdGxlID0gIlhHQm9vc3QiLAogIFggPSBYLAogIHkgPSB5LAogIG1vZGVsID0gInhnYlRyZWUiLAogIGN0cmwgPSBjdHJsLAogIGdyaWQgPSBwYXJhbQopCmBgYAoKYGBge3J9CiNHZXQgYSBwZXJmb3JtYW5jZSBtZXRyaWNzIHRhYmxlCm1ldHJpY3NmIDwtIGNiaW5kKGxyZiRtZXRyaWNzLCB0cmVlZiRtZXRyaWNzLCByZmYkbWV0cmljcykKbWV0cmljc2YKI1Bsb3QgUk9DIGN1cnZlcwpyb2NzZiA8LSBsaXN0KGxyZiRyb2MsIHRyZWVmJHJvYywgcmZmJHJvYykKIyBHZW5lcmF0ZSBsYWJlbHMKbGFiZWxzIDwtIHBhc3RlMChuYW1lcyhtZXRyaWNzKSwgIiwgQVVDID0gIiwgcGFzdGUocm91bmQobWV0cmljc1sxMiwgXSwgMikpKQojIGh0dHBnZDo6aGdkKCkKZ2dyb2Mocm9jc2YpICsKICB0aGVtZV9jbGVhbigpICsKICBzY2FsZV9jb2xvcl90YWJsZWF1KGxhYmVscyA9IGxhYmVscykKYGBgCgojIyMjIyBNdWx0aW5vbWlhbCBMb2dpc3RpYyBSZWdyZXNzaW9uCgpgYGB7ciB9Cm1scmYgPC0gbXVsdGlmaXQoCiAgWCA9IFgsCiAgeSA9IHksCiAgbW9kZWwgPSAibXVsdGlub20iLAogIGN0cmwgPSBjdHJsLAogIHRyYWNlID0gRkFMU0UsCiAgdHVuZUxlbmd0aCA9IDMKKQptbHJmJGNtCmBgYAoKIyMjIyMgRGVjaXNpb24gVHJlZQoKYGBge3IgbXVsdGlfdHJlZX0KbXRyZWVmIDwtIG11bHRpZml0KAogIFggPSBYLAogIHkgPSB5LAogIG1vZGVsID0gInJwYXJ0MiIsCiAgY3RybCA9IGN0cmwsCiAgdHVuZUxlbmd0aCA9IDMKKQptdHJlZWYkY20KYGBgCgojIyMjIyBYR0Jvb3N0IEZvcmVzdAoKYGBge3IgfQptcmZmIDwtIG11bHRpZml0KAogIFggPSBYLAogIHkgPSB5LAogIG1vZGVsID0gInhnYlRyZWUiLAogIGN0cmwgPSBjdHJsLAogIGdyaWQgPSBwYXJhbQopCm1yZmYkY20KYGBgCgpgYGB7cn0KI0dldCBhIHBlcmZvcm1hbmNlIG1ldHJpY3MgdGFibGUKbXVsdGltZXRyaWNzZiA8LSBjYmluZChtbHJmJGNtJGJ5Q2xhc3NbMyxdLCBtdHJlZWYkY20kYnlDbGFzc1szLF0sIG1yZmYkY20kYnlDbGFzc1szLF0pCm11bHRpbWV0cmljc2YKI1Bsb3QgUk9DIGN1cnZlcwptcm9jcyA8LSBsaXN0KG1scmYkcm9jLCBtdHJlZWYkcm9jLCBtcmZmJHJvYykKIyBHZW5lcmF0ZSBsYWJlbHMKbGFiZWxzIDwtIHBhc3RlMChuYW1lcyhtZXRyaWNzKSwgIiwgQVVDID0gIiwgcGFzdGUocm91bmQobWV0cmljc1sxMiwgXSwgMikpKQojIGh0dHBnZDo6aGdkKCkKZ2dyb2MobXJvY3MpICsKICB0aGVtZV9jbGVhbigpICsKICBzY2FsZV9jb2xvcl90YWJsZWF1KGxhYmVscyA9IGxhYmVscykKYGBgCgo=